//
//  LLNotifyModel.m
//  SDWebExercise
//
//  Created by admin on 2019/11/21.
//  Copyright © 2019 admin. All rights reserved.
//

#import "LLNotifyCenter.h"
#import <objc/runtime.h>

@interface LLNotifyCenter ()

/// 存放每个通知名称对应的block数组
@property (nonatomic, strong) NSMutableDictionary *notifyDic;
@property (nonatomic, strong) dispatch_semaphore_t semaphore;   // 加锁

@end

@implementation LLNotifyCenter

+ (instancetype)shareCenter
{
    static LLNotifyCenter *notify = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        notify = [[self alloc] init];
        notify.notifyDic = [NSMutableDictionary dictionary];
        notify.semaphore = dispatch_semaphore_create(1);
    });
    return notify;
}

- (NSString *)getNotifySelStringWithObserver:(id)observer name:(NSString *)name
{
    // 以 observer_name: 作为 key
    return [NSString stringWithFormat:@"%@_%@:", NSStringFromClass([observer class]), name];
}

- (void)addObserver:(id)observer name:(NSString *)name object:(id)object usingBlock:(NotifyBlock)block
{
    if (observer == nil || name.length == 0) {
        return;
    }
    
    LLNotifyCenter *center = [LLNotifyCenter shareCenter];
    
    dispatch_semaphore_wait(center.semaphore, DISPATCH_TIME_FOREVER);
    
    NSString *notifySelString = [self getNotifySelStringWithObserver:observer name:name];
    // 保存block
    NSMutableArray *array;
    if (center.notifyDic[notifySelString] == nil) {
        array = [NSMutableArray array];
        [center.notifyDic setObject:array forKey:notifySelString];
    } else {
        array = center.notifyDic[notifySelString];
    }
    if (![array containsObject:block]) {
        [array addObject:block];
    }
    
    SEL sel = NSSelectorFromString(notifySelString);
    [[NSNotificationCenter defaultCenter] addObserver:observer selector:sel name:name object:object];
    // 方法交换：把 observer 里面的方法实现交给 LLNotifyCenter 的 run 来执行
    Method om = class_getInstanceMethod([observer class], sel);
    class_replaceMethod([observer class], sel, (IMP)run, method_getTypeEncoding(om));
    
    dispatch_semaphore_signal(center.semaphore);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    class_addMethod(self, sel, (IMP)run, "v@:@");
    return [super resolveInstanceMethod:sel];
}

void run(id self, SEL _cmd, NSNotification *notify) {
    
    LLNotifyCenter *center = [LLNotifyCenter shareCenter];
    
    dispatch_semaphore_wait(center.semaphore, DISPATCH_TIME_FOREVER);
    
    NSString *selStr = NSStringFromSelector(_cmd);
    // 回调block
    NSMutableArray *blockArray = center.notifyDic[selStr];
    for (NotifyBlock block in blockArray) {
        block(notify);
    }
    
    dispatch_semaphore_signal(center.semaphore);
}

- (void)postNotificationName:(NSString *)name object:(id)object
{
    [[NSNotificationCenter defaultCenter] postNotificationName:name object:object];
}

///移除观察者，这个要在dealloc里面移除
- (void)removeObserver:(id)observer name:(NSString *)name object:(id)object
{
    if (observer == nil || name.length == 0) {
        return;
    }
    
    LLNotifyCenter *center = [LLNotifyCenter shareCenter];
    
    dispatch_semaphore_wait(center.semaphore, DISPATCH_TIME_FOREVER);
    // 移除观察者
    [[NSNotificationCenter defaultCenter] removeObserver:observer name:name object:object];
    
    //从字典里删除
    NSString *notifySelString = [self getNotifySelStringWithObserver:observer name:name];
    [center.notifyDic removeObjectForKey:notifySelString];
    
    dispatch_semaphore_signal(center.semaphore);
}


///移除这个对象的所有通知
- (void)removeObserver:(id)observer
{
    if (observer == nil) {
        return;
    }
    
    LLNotifyCenter *center = [LLNotifyCenter shareCenter];
    
    dispatch_semaphore_wait(center.semaphore, DISPATCH_TIME_FOREVER);
    // 移除观察者
    [[NSNotificationCenter defaultCenter] removeObserver:observer];
    
    //从字典里删除
    NSArray *array = center.notifyDic.allKeys;
    for (NSString *key in array) {
        if ([key hasPrefix:NSStringFromClass([observer class])]) {
            [center.notifyDic removeObjectForKey:key];
        }
    }
    dispatch_semaphore_signal(center.semaphore);
}

@end
